package column


import (
	"github.com/treelab/updatecolumn/models"
)

  func instanceType() int32 {
    return models.RollUp
  }

  deleteColumnEdges(command: UpdateColumn_v2, o: CommonOptions) {
    const { column } = command;
    o.delQuads.push(
      ...this.dgraphHelperService.getRemoveEdgeQuads(column.uid!, [
        'formula',
        'errorCode',
        'recordReferenceColumnId',
        'resultType',
        'numberFormat',
        'precision',
        'currencyFormat',
        'dateFormat',
        'timeFormat',
        'includeTime',
        'useGMT',
        Relation.HAS_FOREIGN_LOOKUP_COLUMN,
        Relation.HAS_RECORD_REFERENCE_COLUMN,
        Relation.HAS_DEPENDENT_COLUMN,
      ]),
    );
    const recordReferenceColumn = column.hasRecordReferenceColumn.get();
    if (recordReferenceColumn.length) {
      column.hasRecordReferenceColumn.deleteAll();
    }

    o.delQuads.push(
      ...this.dgraphHelperService.getRemoveEdgeQuads(
        column.recordReferenceColumnId,
        Relation.HAS_ROLLUP_COLUMN,
        column.uid!,
      ),
    );
    const rollupDependencies = column.hasDependentColumn.get();
    const ids = rollupDependencies.map(rollupDependent => rollupDependent.uid!);
    o.delQuads.push(...this.dgraphHelperService.getRemoveEdgeQuads(ids, Relation.HAS_ROLLUP_UPDATE, column.uid!));
    return this;
  }

  public hasNothingToUpdate(command: UpdateColumn_v2) {
    const { column } = command;
    const rollupColumnConfig = command.columnConfig as RollupColumnConfig;
    let oldFormula = column.formula || '';
    column.hasDependentColumn.get().forEach((c: any, order: number) => {
      oldFormula = oldFormula.replace(new RegExp(`column_value_${order}`, 'g'), `column_value_${c.uid}`);
    });
    return (
      column.columnType === this.instanceType &&
      oldFormula === rollupColumnConfig.formula &&
      column.resultType === rollupColumnConfig.resultType &&
      !column.errorCode &&
      column.currencyFormat === rollupColumnConfig.currencyFormat &&
      column.recordReferenceColumnId === rollupColumnConfig.recordReferenceColumnId &&
      column.numberFormat === rollupColumnConfig.numberFormat &&
      column.precision === rollupColumnConfig.precision &&
      column.dateFormat === rollupColumnConfig.dateFormat &&
      column.timeFormat === rollupColumnConfig.timeFormat &&
      column.includeTime === rollupColumnConfig.includeTime &&
      column.useGMT === rollupColumnConfig.useGMT
    );
  }

  async updateColumn(
    command: UpdateColumn_v2,
    o: CommonOptions,
    helper: { rows?: Row[]; columnCopy?: Column; referenceColumns?: Column[]; userGroup?: any },
  ) {
    await super.updateColumn(command, o, helper);

    const { column, columnConfig } = command;
    const { recordReferenceColumnId, errorCode } = columnConfig as RollupColumnConfig;
    if (!errorCode) {
      column.recordReferenceColumnId = recordReferenceColumnId;
      const recordReferenceColumn = o.ormTransaction.nodeFor(Column, { uid: recordReferenceColumnId });
      column.hasRecordReferenceColumn.add(recordReferenceColumn);
      recordReferenceColumn.hasRollupColumn.add(column);
    }
    return this;
  }

  async convertColumn(
    command: UpdateColumn_v2,
    o: CommonOptions,
    helper: { rows?: Row[]; columnCopy?: Column; referenceColumns?: Column[]; userGroup?: any },
  ) {
    const { column } = command;
    const rollupConfig = command.columnConfig as RollupColumnConfig;
    const { formula, recordReferenceColumnId } = rollupConfig;

    let oldFormula = column.formula || '';
    const dependentColumns = column.hasDependentColumn.get();
    dependentColumns.forEach((c, order: number) => {
      oldFormula = oldFormula.replace(new RegExp(`column_value_${order}`, 'g'), `column_value_${c.uid}`);
    });
    if (
      formula === oldFormula &&
      recordReferenceColumnId === column.recordReferenceColumnId &&
      !column.errorCode &&
      rollupConfig.numberFormat === column.numberFormat &&
      rollupConfig.precision === column.precision &&
      rollupConfig.currencyFormat === column.currencyFormat &&
      rollupConfig.dateFormat === column.dateFormat &&
      rollupConfig.timeFormat === column.timeFormat &&
      rollupConfig.includeTime === column.includeTime &&
      rollupConfig.useGMT === column.useGMT
    ) {
      return this;
    }
    /* Check that the dependent columns don't have a formula column which has a dependency used
     in the rollup formula.
    TODO: In the future we can support this but right now we are going to block this. */
    this.isValidRollupFormula(column);
    const symmetricColumn = await this.getEdges(recordReferenceColumnId, Relation.HAS_SYMMETRIC_COLUMN);
    const dependencies = Object.keys(FormulaExecutor.getDependencies(rollupConfig.formula));
    await Promise.all(
      (dependencies || []).map(async (dependentId: any) => {
        const dependentColumn = await this.helperReadService.getNodeById(dependentId, ['errorCode']);
        if (dependentColumn.errorCode) {
          throw new RollupColumnInvalidError(dependentId!);
        }
        this.isSelfReference(symmetricColumn ? symmetricColumn[0] : undefined, {
          foreignLookupColumnId: dependentId!,
          recordReferenceColumnId,
          type: ColumnType.ROLLUP,
        });
        // must validate new dependId column, because they wasn't in ORM model
        await this.checkCircularReference(command.tableId, dependentId);
      }),
    );

    await this.defaultConvert({ command }, o, helper);
    return this;
  }

  updateDependentColumn(column: Column, formula: string, dependencies: string[], o: CommonOptions) {
    _.forEach(dependencies, (dependentId, i) => {
      formula = formula.replace(new RegExp(`column_value_${dependentId}`, 'g'), `column_value_${i}`);
      const dependentColumns = column.hasDependentColumn.get();
      const existingDependentColumn = dependentColumns.find(({ uid }) => uid === dependentId);
      if (!existingDependentColumn) {
        const dependentColumn = o.ormTransaction.nodeFor(Column, { uid: dependentId });
        column.hasForeignLookupColumn.add(dependentColumn);
        column.hasDependentColumn.withFacet(new OrderFacet({ order: i })).add(dependentColumn);
        dependentColumn.hasRollupUpdate.add(column);
      }
    });
    return formula;
  }

  public getColumnTypeOptions(column: Column, columnConfig?: any): { name: any; type: any; typeOptions: string } {
    const result = super.getColumnTypeOptions(column, columnConfig);

    if (columnConfig) {
      return result;
    }

    let { formula } = column;
    const { errorCode, resultType } = column;
    const dependentColumns = column.hasDependentColumn.get();

    if (errorCode === ColumnErrorCodes.ROLLUP_COLUMN_REMOVED) {
      return {
        name,
        type: ColumnType.ROLLUP,
        typeOptions: JSON.stringify({ errorCode: ColumnErrorCodes.ROLLUP_COLUMN_REMOVED }),
      };
    }
    const dependencies = Object.keys(FormulaExecutor.getDependencies(formula));
    const dependentResult = _.map(dependencies, dependenId => {
      formula = formula.replace(
        new RegExp(`column_value_${dependenId}`, 'g'),
        `column_value_${dependentColumns[+dependenId].uid}`,
      );
      const dependentColumn = dependentColumns[+dependenId];
      const { typeOptions: dependentColumnTypeOptions } = this.getColumnTypeOptions(dependentColumn);
      const dependentErrorCode = JSON.parse(dependentColumnTypeOptions).errorCode;
      if (dependentErrorCode) {
        return {
          name,
          type: ColumnType.ROLLUP,
          typeOptions: JSON.stringify({ errorCode: ColumnErrorCodes.ROLLUP_COLUMN_REMOVED }),
        };
      }
    }).filter(i => i);
    if (dependentResult.length) {
      return dependentResult[0]!;
    }
    const typeOptions = JSON.parse(result.typeOptions);
    typeOptions.resultType = resultType || FormulaResultType.TEXT;
    return Object.assign(result, { typeOptions: JSON.stringify(typeOptions) });
  }

  public isValidRollupFormula(column: Column) {
    const rollupdependentColumns = column.hasDependentColumn.get(); // Object.keys(FormulaExecutor.getDependencies(formula));
    const rollupdependencies = rollupdependentColumns.map(c => c.uid!);
    _.forEach(rollupdependentColumns, dependency => {
      // Check that the dependent columns don't have a formula column
      // which has a dependency used in the rollup formula.
      // TODO: In the future we can support this but right now we are going to block this.
      const dependentColumn = dependency.hasDependentColumn.get(); // await this.columnReadService.getColumnById(dependency);
      const dependencies = dependentColumn.map(i => i.uid!);
      if (dependency.columnType === ColumnType.FORMULA) {
        dependencies.forEach((dependentColumnId: string) => {
          if (rollupdependencies.includes(dependentColumnId)) {
            throw new InvalidRollupFormulaError(
              'Rollup formula cannot have use a formula column dependency from the same rollup formula',
            );
          }
        });
      }
      // Check that the dependent columns don't have a rollup column
      // which has a dependency used in the rollup formula as well.
      // TODO: In the future we can support this but right now we are going to block this. */
      if (dependency.columnType === ColumnType.ROLLUP) {
        // Append the record reference column that the rollup is using as well
        dependencies.concat(dependency.recordReferenceColumnId!).forEach((dependentColumnId: string) => {
          if (rollupdependencies.includes(dependentColumnId)) {
            throw new InvalidRollupFormulaError(
              'Rollup formula cannot use a rollup column with dependencies from the same rollup formula',
            );
          }
        });
      }
    });
  }

